﻿using System;
using System.Globalization;

using zijian666.Converts.Abstractions;
using zijian666.Converts.Convertor.Base;
using zijian666.Converts.Core;
using zijian666.Converts.Extensions;

using static System.UInt64;

namespace zijian666.Converts.Convertor;

/// <summary>
/// <seealso cref="ulong"/> 转换器
/// </summary>
public class UInt64Convertor : FromConvertor<ulong>
                            , IFromConvertible<ulong>
                            , IFrom<byte[], ulong>
                            , IFrom<Guid, ulong>   
{
    /// <inheritdoc />
    public ConvertResult<ulong> From(IConvertContext context, bool input) => input ? 1 : (ulong)0;

    /// <inheritdoc />
    public ConvertResult<ulong> From(IConvertContext context, char input) => input;

    /// <inheritdoc />
    public ConvertResult<ulong> From(IConvertContext context, sbyte input) => (ulong)input;

    /// <inheritdoc />
    public ConvertResult<ulong> From(IConvertContext context, byte input) => input;

    /// <inheritdoc />
    public ConvertResult<ulong> From(IConvertContext context, short input) => (ulong)input;

    /// <inheritdoc />
    public ConvertResult<ulong> From(IConvertContext context, ushort input) => input;

    /// <inheritdoc />
    public ConvertResult<ulong> From(IConvertContext context, int input) => (ulong)input;

    /// <inheritdoc />
    public ConvertResult<ulong> From(IConvertContext context, uint input) => input;

    /// <inheritdoc />
    public ConvertResult<ulong> From(IConvertContext context, long input)
    {
        if (input < 0)
        {
            return ExceptionHelper.Overflow($"{input} < {MinValue}", context.Settings.CultureInfo);
        }
        return (ulong)input;
    }

    /// <inheritdoc />
    public ConvertResult<ulong> From(IConvertContext context, ulong input) => input;

    /// <inheritdoc />
    public ConvertResult<ulong> From(IConvertContext context, float input)
    {
        if (input is < MinValue or > MaxValue)
        {
            return ExceptionHelper.Overflow(input < MinValue ? $"{input} < {MinValue}" : $"{input} > {MaxValue}", context.Settings.CultureInfo);
        }
        return (ulong)input;
    }

    /// <inheritdoc />
    public ConvertResult<ulong> From(IConvertContext context, double input)
    {
        if (input is < MinValue or > MaxValue)
        {
            return ExceptionHelper.Overflow(input < MinValue ? $"{input} < {MinValue}" : $"{input} > {MaxValue}", context.Settings.CultureInfo);
        }
        return (ulong)input;
    }

    /// <inheritdoc />
    public ConvertResult<ulong> From(IConvertContext context, decimal input)
    {
        if (input is < MinValue or > MaxValue)
        {
            return ExceptionHelper.Overflow(input < MinValue ? $"{input} < {MinValue}" : $"{input} > {MaxValue}", context.Settings.CultureInfo);
        }
        return decimal.ToUInt64(input);
    }

    /// <inheritdoc />
    public ConvertResult<ulong> From(IConvertContext context, DateTime input) => context.ConvertFail(this, input);

    /// <inheritdoc />
    public ConvertResult<ulong> From(IConvertContext context, string input)
    {
        var s = input?.Trim() ?? "";
        if (TryParse(s, NumberStyles.Any, context.Settings.NumberFormatInfo ?? NumberFormatInfo.CurrentInfo, out var result))
        {
            return result;
        }
        if (s.Length > 2)
        {

            switch ((s[0], s[1]))
            {
                case ('0', 'x' or 'X'):
                    if (TryParse(s.Substring(2), NumberStyles.HexNumber, NumberFormatInfo.InvariantInfo, out result))
                    {
                        return result;
                    }
                    return context.ConvertFail(this, input);
                case ('0', 'b' or 'B'):
                    try
                    {
                        return System.Convert.ToUInt64(s.Substring(2), 2);
                    }
                    catch (Exception e)
                    {
                        return e;
                    }
                case ('&', 'H' or 'h'):
                    if (TryParse(s.Substring(2), NumberStyles.HexNumber, NumberFormatInfo.InvariantInfo, out result))
                    {
                        return result;
                    }
                    return context.ConvertFail(this, input);
            }
        }
        return context.ConvertFail(this, input);
    }

    /// <inheritdoc />
    public ConvertResult<ulong> From(IConvertContext context, byte[] input)
    {
        if (input is null || input.Length > sizeof(ulong))
        {
            return context.ConvertFail(this, input);
        }
        Array.Resize(ref input, sizeof(ulong));
        return BitConverter.ToUInt64(input, 0);
    }

    /// <inheritdoc />
    public ConvertResult<ulong> From(IConvertContext context, Guid input) => new(BitConverter.ToUInt64(input.ToByteArray(), 0));
}
